/*
 * pxxinit.cpp
 * Copyright (c) 2009, "Eugen Stoian <stoian.e@gmail.com>"
 * 
 * This library is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation; either version 3 of the License, or (at your option)
 * any later version.
 * 
 * This library is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this library; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
 * http://www.fsf.org/licensing/licenses/lgpl.txt
 */

#include "core.h"
#include <dlfcn.h>
#include <opsbase.h>
#include <cstring>

namespace pxx {

core* core::_instance = new core;

core::core() {
	_v_addv_ps = &v_addv_ps;
	_v_subv_ps = &v_subv_ps;
	_v_mulv_ps = &v_mulv_ps;
	_v_divv_ps = &v_divv_ps;
	_v_dotv_ps = &v_dotv_ps;
	_v_muls_ps = &v_muls_ps;
	_v_muls_addv_ps = &v_muls_addv_ps;
	_v_addv_pd = &v_addv_pd;
	_v_subv_pd = &v_subv_pd;
	_v_mulv_pd = &v_mulv_pd;
	_v_divv_pd = &v_divv_pd;
	_v_dotv_pd = &v_dotv_pd;
	_v_muls_pd = &v_muls_pd;
	_v_muls_addv_pd = &v_muls_addv_pd;
	//_m_transpose_ps = &m_transpose_ps;
	//_m_transpose_pd = &m_transpose_pd;
	_handle = 0;
#ifdef _OPENMP
	omp_set_dynamic(1);
#endif
}

core::~core() {
	if (_handle)
		dlclose(_handle);
}
/*!
 * \fn core::reset()
 * \brief Unloads any SIMD optimization module.
 *
 */
void core::reset() {
	if (_handle) {
		dlclose(_handle);
	}
	_handle = 0;
	_v_addv_ps = &v_addv_ps;
	_v_subv_ps = &v_subv_ps;
	_v_mulv_ps = &v_mulv_ps;
	_v_divv_ps = &v_divv_ps;
	_v_dotv_ps = &v_dotv_ps;
	_v_muls_ps = &v_muls_ps;
	_v_muls_addv_ps = &v_muls_addv_ps;
	_v_addv_pd = &v_addv_pd;
	_v_subv_pd = &v_subv_pd;
	_v_mulv_pd = &v_mulv_pd;
	_v_divv_pd = &v_divv_pd;
	_v_dotv_pd = &v_dotv_pd;
	_v_muls_pd = &v_muls_pd;
	_v_muls_addv_pd = &v_muls_addv_pd;
	//_m_transpose_ps = &m_transpose_ps;
	//_m_transpose_pd = &m_transpose_pd;
}

/*!
 * \fn core::init(const char * path, int threadsCount)
 * \brief Loads SIMD optimization module specified by path and sets the number of threads used by default in subsequent parallel sections.
 *
 * If module cannot be loaded returns \b false. In case of success returns \b true.
 * See core::load() and core::setThreadsCount()
 */
bool core::init(const char * path, int threadsCount){
	setThreadsCount(threadsCount);
	return load(path);
}

/*!
 * \fn core::load(const char * path)
 * \brief Dlopens SIMD optimization module specified by path. Null string means no optimizations.
 *
 * If module cannot be loaded returns \b false. In case of success returns \b true.
 */
bool core::load(const char * path){
	_instance->reset();
	if (strlen(path) != 0) {
		_instance->_handle = dlopen(path, RTLD_NOW);
		if (_instance->_handle) {
			_instance->_v_addv_ps = (op_v_vv_ps) dlsym(_instance->_handle,
					"v_addv_ps");
			_instance->_v_subv_ps = (op_v_vv_ps) dlsym(_instance->_handle,
					"v_subv_ps");
			_instance->_v_mulv_ps = (op_v_vv_ps) dlsym(_instance->_handle,
					"v_mulv_ps");
			_instance->_v_divv_ps = (op_v_vv_ps) dlsym(_instance->_handle,
					"v_divv_ps");
			_instance->_v_dotv_ps = (op_s_vv_ps) dlsym(_instance->_handle,
					"v_dotv_ps");
			_instance->_v_muls_ps = (op_v_vs_ps) dlsym(_instance->_handle,
					"v_muls_ps");
			_instance->_v_muls_addv_ps = (op_v_vsv_ps) dlsym(_instance->_handle,
					"v_muls_addv_ps");
			_instance->_v_addv_pd = (op_v_vv_pd) dlsym(_instance->_handle,
					"v_addv_pd");
			_instance->_v_subv_pd = (op_v_vv_pd) dlsym(_instance->_handle,
					"v_subv_pd");
			_instance->_v_mulv_pd = (op_v_vv_pd) dlsym(_instance->_handle,
					"v_mulv_pd");
			_instance->_v_divv_pd = (op_v_vv_pd) dlsym(_instance->_handle,
					"v_divv_pd");
			_instance->_v_dotv_pd = (op_s_vv_pd) dlsym(_instance->_handle,
					"v_dotv_pd");
			_instance->_v_muls_pd = (op_v_vs_pd) dlsym(_instance->_handle,
					"v_muls_pd");
			_instance->_v_muls_addv_pd = (op_v_vsv_pd) dlsym(_instance->_handle,
					"v_muls_addv_pd");
			/*
			_instance->_m_transpose_ps = (op_m_m_ps) dlsym(_instance->_handle, "m_transpose_ps");
			_instance->_m_transpose_pd = (op_m_m_pd) dlsym(_instance->_handle, "m_transpose_pd");
			*/
		}
		else
			return false;
	}
	return true;
}

/*!
 * \fn core::getThreadsCount()
 * \brief Returns the number of threads used by default in subsequent parallel sections.
 *
 */
int core::getThreadsCount() {
#ifdef _OPENMP
	return _instance->_threadsCount;
#else
	return 1;
#endif
}

/*!
 * \fn core::setThreadsCount(int count)
 * \brief Sets the number of threads used by default in subsequent parallel sections.
 */
void core::setThreadsCount(int count) {
#ifdef _OPENMP
	_instance->_threadsCount = count;
	omp_set_num_threads(count);
#endif
}

/*!
 * \fn core::end()
 * \brief Destroys the core singleton object.
 *
 *  Any dlopened module will be discarded. This method should be used only once, before exiting main().\n
 *  Any attempt to use core class methods after calling \b core::end() will end with a segmentation fault so use it with caution.
 */
void core::end(){
	if(_instance){
		delete _instance;
		_instance = 0;
	}
}
}

